import * as types from './mutation-types'

import * as cache from '@/utils/storage.js'
import {jumpTo,toast,delay} from "@/utils/util.js"
import chat from "@/IM/index.js"
import {msgFilter,dictateTypes, msgTypes} from "@/IM/msg.js"
import * as userAPI from "@/api/user.js"
import * as chatAPI from "@/api/chat.js"
import * as sessionAPI from "@/api/session.js"
import {oncePlay,getPaused} from "@/player/audio.js"
import { Conversation } from '@/entity/Conversation'
import { ConversationType } from '@/entity/ConversationType'
import {initPools,resetPools,savePools,getPool} from "@/IM/messagePool.js"
import DC from '@/DC'
import { someLast } from '@/utils/array'
// #ifdef APP-PLUS
import { downToPath, pathByUrl } from '@/services/mediaService'
// #endif

const CONV_KEY = "conversations_"
const HISTORY_KEY = "chat_history_"

let globalData = null
const createNewConversation = async (id,type)=>{
	const conversation = Conversation.createNullConversation(id,type)
	if(type==ConversationType.Single){
		conversation._target = DC.userDB.getUser(id)
		await sessionAPI.fetchOneByUser(id).then((conv)=>{
			conv&&Conversation.convertFromAPI(conversation,conv)
		})
	}
	if(type==ConversationType.Group){
		conversation._target = DC.groupDB.getGroup(id)
		await sessionAPI.fetchOneByGroup(id).then((conv)=>{
			conv&&Conversation.convertFromAPI(conversation,conv)
		})
	}
	return conversation
}

export default {
	
	async loginChat({
		commit,
		dispatch,
		rootState
	}) {
		const token = rootState.user.token;
		if (!token) {
			throw new Error("用户未登录,不能创建聊天")
		}
		const userId = rootState.user.userInfo.userId;
		dispatch('initConversation')
		let res = null
		try{
			
			
			
			res = await chat.login({
				token,
				userId
			})
		}catch(e){
			for (let i = 0; i < 3; i++) {
				res = await chat.login({
					token,
					userId
				}).catch((e)=>{console.error(e)})
				if(!!res){
					break
				}
			}
		}
		if(!res){
			throw new Error('连接失败')
		}
		dispatch('initGotReds')
		initPools(userId)
		return res;
	},
	
	async logoutChat({
		commit
	}) {
		const res = await chat.logout();
		commit(types.UPDATE_CONVERSATION_MAP, {}) 
		resetPools()
		return res;
	},
	
	async saveConversationMap({
		state,
		rootState,
		getters:{conversations}
	}) {
		const loginUserId = rootState.user.userInfo.userId;
		const map = conversations.slice(0,20).reduce((res,n)=>{
			res[n.id] = n.getSaveInfo()
			return res
		},{})
		cache.set(`${CONV_KEY}${loginUserId}`, map);
	},
	
	async setConversation(
		{commit,dispatch,state:{conversationMap}},
		{id,...rest}
	){
		if(!id){
			throw new Error('请提供会话id')
		}
		let item = conversationMap[id]
		if(!item){ 
			if(!rest.type||!rest.name){
				throw new Error('新建会话需要提供必要属性,会话类型,会话名')
			}
			item = Conversation.mix(Conversation.createNullConversation(id,rest.type),{id,...rest})
		}else{ 
			item = Conversation.mix(item,{...item,...rest})
		}
		if(!item.sid){
			if(item.type==ConversationType.Single){
				sessionAPI.fetchOneByUser(id).then((conv)=>{
					conv&&(item.sid = conv.id)
				})
			}
			if(item.type==ConversationType.Group){
				sessionAPI.fetchOneByGroup(id).then((conv)=>{
					conv&&(item.sid = conv.id)
				})
			}
		}
		commit(types.UPDATE_CONVERSATION_MAP,{
			...conversationMap,
			[id]:item
		})
		dispatch('saveConversationMap');
		return item
	},
	
	async removeConversation({
		commit,
		dispatch,
		state:{conversationMap}
	}, id) {
		const temp = {
			...conversationMap
		}
		delete temp[id]
		commit(types.UPDATE_CONVERSATION_MAP,temp)
		dispatch('saveConversationMap');
		return id
	},
	
	async sendMessage({
		commit,
		dispatch,
		state:{
			conversationMap
		}
	}, {
		id,
		message
	}) {
		const conversation = conversationMap[id];
		conversation.lastMessage = message.content;
		conversation.lastTime = message.createTime;
		commit(types.UPDATE_CONVERSATION_MAP, {
			...conversationMap,
			[id]:conversation
		})
		dispatch('saveConversationMap')
		const pool = getPool(id)
		pool.push(message)
		pool.splice(0,pool.length-10)
		return {
			id,
			message
		}
	},
	
	async recallMessage({commit,dispatch,state:{conversationMap,history}},{to,msgId}){
		
	},
	
	async receiveMessage({
		commit,
		state:{
			conversationMap
		},
		dispatch,
		rootState
	}, message) {
		const myId = rootState.user.userInfo.userId
		let id = '' 
		if(message.chatType==ConversationType.Group){ 
			id = message.groupId
		}else if(message.chatType==ConversationType.Single){
			id = message.from
			if(myId==id){
				id=message.to
			}
		}
		let conversation = conversationMap[id]
		if (!conversation) { 
			conversation = await createNewConversation(id,message.chatType)
		}
		const pool = getPool(id)
		if(someLast(pool,({id})=>id==message.id)){
			return '本地已存在该消息'
		}
		if(message.from!=myId){
			conversation.unread++;
		}
		conversation.lastMessage = message.content
		conversation.lastTime = message.createTime
		
		let isAtMe = false
		if(message.extras&&message.extras.at&&!conversation.mention){
			const ats = message.extras.at||''
			if(ats.includes('all')){
				isAtMe = true
			}
			if(ats.includes('all')||ats.includes(myId)){
				conversation.mention = conversation.mention||message.id
				conversation.mentionAll = ats.includes('all')
			}
		}
		
		commit(types.UPDATE_CONVERSATION_MAP, {
			...conversationMap,
			[id]:conversation
		})
		
		if(message.msgType==msgTypes.RECALL){
			const idx = pool.findIndex((msg)=>msg.id==message.extras.recallId)
			if(idx>=0){
				pool.splice(idx,1)
			}
		}
		if(message.msgType == msgTypes.FACE){
			message.run = true
		}
		// #ifdef APP-PLUS
		if(message.msgType == msgTypes.IMG){
			message.saveImg = ''
			const imgUrl = message.extras.imgUrl
			pathByUrl(imgUrl).then((path)=>{
				if(path){
					message.saveImg = path
				}else{
					downToPath(imgUrl).then((newPath)=>{
						message.saveImg = newPath
					})
				}
			})
		}
		if(message.msgType==msgTypes.VIDEO){
			message.saveVideo = ''
			message.saveThumb = ''
			const thumb = message.extras.thumb
			const videoUrl = message.extras.videoUrl
			pathByUrl(videoUrl).then((path)=>{
				if(path){
					message.saveVideo = path
				}else{
					downToPath(videoUrl).then((newPath)=>{
						message.saveVideo = newPath
					})
				}
			})
			pathByUrl(thumb).then((path)=>{
				if(path){
					message.saveThumb = path
				}else{
					downToPath(thumb).then((newPath)=>{
						message.saveThumb = newPath
					})
				}
			})
		}
		if(message.msgType==msgTypes.FACE&&!message.extras.type){
			message.saveImg = ''
			const imgUrl = message.extras.imgUrl
			pathByUrl(imgUrl).then((path)=>{
				if(path){
					message.saveImg = path
				}else{
					downToPath(imgUrl).then((newPath)=>{
						message.saveImg = newPath
					})
				}
			})
		}
		// #endif
		pool.push(message)
		pool.splice(0,pool.length-10)
		dispatch('saveHistory')
		dispatch('saveConversationMap')
		
		uni.$emit('chat_receive',id,message)
		if(myId!=message.from&&(isAtMe||!conversation.isMute)){
			getPaused()&&oncePlay('/static/voice/news.mp3')
		}
		if(!conversation.sid){
			dispatch('fetchConversation')
		}
		// #ifdef APP-PLUS
		if(!globalData){
			globalData = getApp().globalData
		}
		
		if(!globalData.front&&!conversation.isMute){
			
			
			
			
			
			
			
		}
		// #endif
	},
	async receiveRead({commit,dispatch},message){
		console.log('消息已读了',message)
		const pool = getPool(message.toObjType==ConversationType.Group?message.toObjId:message.userId)
		const msg = pool.find(({id})=>id==message.msgId)
		if(msg){
			msg.readBody.readCount++
			msg.readBody.unreadCount--
		}
	},
	
	
	async moreHistory({ 
		commit
	}) {
		
	},
	
	async initHistory({
		commit
	}){
	},
	
	async saveHistory(){
		return savePools()
	},
	
	async removeHistory({
		state:{history},
		commit,
		dispatch
	},id){
		const pool = getPool(id)
		pool.splice(0,pool.length)
		dispatch('saveHistory')
		
		
		
		
		
		
	},
	
	async receiveOffline({
		commit,
		state:{
			conversationMap,
			history
		},
		dispatch
	}, {
		friends, 
		groups 
	}) {
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	},
	
	async receivePenetrate({
		commit,
		state,
		rootState,
		dispatch
	},{extras,chatType,from,to,groupId,msgType}){
		const selfId = rootState.user.userInfo.userId;
		if(msgType==dictateTypes.refreshFriend){
			dispatch('addr/resetFriends',undefined,{ root: true })
		}else if(msgType==dictateTypes.readConv){
			
			dispatch('setConversation',{id:extras.id,unread:0})
		}else if(msgType==dictateTypes.addFriend){
			from!=selfId&&dispatch('addr/loadOneFriend',from,{ root: true })
		}else if(msgType==dictateTypes.delFriend){
			dispatch('addr/removeFriend',from,{ root: true })
		}else if(msgType==dictateTypes.delMsg){
			dispatch('removeMessage',{extras,chatType,from,groupId,msgType})
		}
		if(selfId!=to){
			return
		}
		if(msgType==dictateTypes.call){
			if(!state.channel){
				commit('setChannel',{
					channelId:extras.roomId,
					userId:extras.from.userId,
					userName:extras.from.userName,
					avatar:extras.from.avatarUrl,
					isAudio:extras.isAudio
				})
				jumpTo('/pages/room/receive')
			}else{
				chat.dictate({
					to:extras.from.userId,
					content:'[占线]',
					msgType:dictateTypes.busy,
					extras
				}) 
			}
		}else if(msgType==dictateTypes.busy){
			toast("对方正忙")
			uni.$emit("callBusy",extras)
		}else if(msgType==dictateTypes.rejectCall){
			toast("对方已拒绝")
			uni.$emit("callReject",extras)
			console.log("对方已拒绝")
		}else if(msgType==dictateTypes.cancelCall){
			toast("对方已取消")
			uni.$emit("callCancel",extras)
			// #ifdef APP-PLUS
			plus.push.clear()
			// #endif
		}
	},
	async initGotReds({rootState:{user:{userInfo:{userId}}},commit}){
		const list = await cache.get(`chat_got_reds_${userId}`)||[]
		commit(types.SET_GOT_REDS,list)
	},
	pushGotRed({state:{gotReds},rootState:{user:{userInfo:{userId}}},commit},red){
		const list = [...gotReds,red]
		commit(types.SET_GOT_REDS,list)
		cache.set(`chat_got_reds_${userId}`,list)
	},
	
	async initConversation({
		state,
		rootState:{user:{userInfo:{userId}}},
		commit,
		dispatch,
	}){
		const convMap = {...state.conversationMap}
		const convData = await cache.get(`${CONV_KEY}${userId}`);
		for (let key in convData) {
			const conv = Conversation.mix(new Conversation(),convData[key])
			convMap[key] = conv
		}
		delay(1000).then(()=>{
			Object.entries(convMap).forEach(([key,val])=>{
				val&&val.initTarget&&val.initTarget()
			})
		})
		commit(types.UPDATE_CONVERSATION_MAP, convMap)
	},
	
	async fetchConversationList({state,commit,dispatch},param){
		const list = await sessionAPI.fetchMyList(param)
		const convMap = {...state.conversationMap}
		const convList = list.map((item)=>{
			const conv = Conversation.convertFromAPI(new Conversation(),item)
			if(conv.type==ConversationType.Single){
				conv._target = DC.userDB.getUser(conv.id)
			}else if(conv.type==ConversationType.Group){
				conv._target = DC.groupDB.getGroup(conv.id)
			}
			convMap[conv.id] = conv
			return conv
		})
		commit(types.UPDATE_CONVERSATION_MAP,convMap)
		dispatch('saveConversationMap')
		return convList
	},
	
	async fetchConversation({
		state,
		commit,
    dispatch
	},{id,type}){
		let conv = state.conversationMap[id]
		if(!conv){ 
			
			conv = Conversation.createNullConversation(id,type)
		}
		let plain = null
		
		
		
		if(conv.type==ConversationType.Single){
			plain = await sessionAPI.fetchOneByUser(conv.id)
		}else if(conv.type==ConversationType.Group){
			plain = await sessionAPI.fetchOneByGroup(conv.id)
		}
		
		if(!plain){
			return null
		}
		conv = Conversation.convertFromAPI(conv,plain)
		commit(types.UPDATE_CONVERSATION_MAP, {
			...state.conversationMap,
			[id]:conv
		})
		dispatch('saveConversationMap')
		return conv
	}
}